---
title: Migrating to Apollo Kotlin 4
subtitle: Step-by-step guide on migrating from Apollo Kotlin 3
description: Learn how to migrate from version 3 with key changes, new features, and tools designed for improved stability and maintainability. Follow step-by-step guidance and make a seamless transition
---

Apollo Kotlin 3 was a major rewrite of Apollo in Kotlin multiplatform.

Apollo Kotlin 4 focuses on tooling, stability and making the library more maintainable, so it can evolve smoothly for the many years to come.

While most of the core APIs stayed the same, Apollo Kotlin 4 contains a few binary breaking changes. To account for that, and in order to be more future-proof, [we changed the package name](https://github.com/apollographql/apollo-kotlin/issues/4710) to `com.apollographql.apollo`. See the Apollo Kotlin [evolution policy](../essentials/evolution) for more details.

Apollo Kotlin 4 removes some deprecated symbols. We strongly recommend updating to the latest 3.x release and removing deprecated usages before migrating to version 4.

Important changes are:
* [new package name](#group-id--plugin-id--package-name) and [moved artifacts](#moved-artifacts)
* [fetch errors do not throw anymore](#fetch-errors-do-not-throw)
* [the apolloMetadata Gradle configuration is not used anymore](#multi-module-dependson)

Read below for more details.

## Automatic migration using the Android Studio/IntelliJ plugin

Apollo Kotlin 4 ships with a companion [Android Studio/IntelliJ plugin](../testing/android-studio-plugin#migration-helpers) that automates most of the migration. 

It automates most of the API changes but cannot deal with behavior changes like error handling. 

We recommend using the plugin to automate the repetitive tasks but still go through this document for the details.

## Group id / plugin id / package name

Apollo Kotlin 4 uses a new identifier for its maven group id, Gradle plugin id and package name : **`com.apollographql.apollo`**.

This also allows to run versions 4 and 3 side by side if needed.

In most cases, you can update the identifier in your project by performing a find-and-replace and replacing `com.apollographql.apollo3` with `com.apollographql.apollo`.

### Group id

The maven group id used to identify Apollo Kotlin 4 [artifacts](https://repo1.maven.org/maven2/com/apollographql/apollo/) is `com.apollographql.apollo`:

```kotlin
// Replace:
implementation("com.apollographql.apollo3:apollo-runtime:3.8.4")

// With:
implementation("com.apollographql.apollo:apollo-runtime:4.0.0")
```

### Gradle plugin id

The Apollo Kotlin 4.0 Gradle plugin id is `com.apollographql.apollo`:

```kotlin
// Replace:
plugins {
  id("com.apollographql.apollo3") version "3.8.4"
}

// With:
plugins {
  id("com.apollographql.apollo") version "4.0.0"
}
```

### Package name

Apollo Kotlin 4 classes use the `com.apollographql.apollo` package:

```kotlin
// Replace:
import com.apollographql.apollo3.ApolloClient
import com.apollographql.apollo3.*

// With:
import com.apollographql.apollo.ApolloClient
import com.apollographql.apollo.*
```

## Moved artifacts 

Over the years, a lot of support functionality was added alongside the `apollo-api` and `apollo-runtime` artifacts. While useful, most of this functionality doesn't share the same level of stability and maturity as the core artifacts and bundling them did not make much sense.

Moving forward, only the core artifacts remain in the `apollo-kotlin` repository and are released together. Other artifacts are moved to new maven coordinates and GitHub repositories. 

This will allow us to iterate faster on new functionality while keeping the core smaller and more maintainable.

The artifacts with new coordinates are:

| Old coordinates                                                     | New coordinates                                             | New Repository                                                                                                                        |
|---------------------------------------------------------------------|-------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------|
| com.apollographql.apollo3:apollo-adapters                           | com.apollographql.adapters:apollo-adapters-core             | [apollographql/apollo-kotlin-adapters](https://github.com/apollographql/apollo-kotlin-adapters)                                       |
|                                                                     | com.apollographql.adapters:apollo-adapters-kotlinx-datetime | [apollographql/apollo-kotlin-adapters](https://github.com/apollographql/apollo-kotlin-adapters)                                       |
| com.apollographql.apollo3:apollo-compose-support-incubating         | com.apollographql.compose:compose-support                   | [apollographql/apollo-kotlin-compose-support](https://github.com/apollographql/apollo-kotlin-compose-support)                         |
| com.apollographql.apollo3:apollo-compose-paging-support-incubating  | com.apollographql.compose:compose-paging-support            | [apollographql/apollo-kotlin-compose-support](https://github.com/apollographql/apollo-kotlin-compose-support)                         |
| com.apollographql.apollo3:apollo-cli-incubating                     | com.apollographql.cli:apollo-cli                            | [apollographql/apollo-kotlin-cli](https://github.com/apollographql/apollo-kotlin-cli)                                                 |
| com.apollographql.apollo3:apollo-engine-ktor                        | com.apollographql.ktor:apollo-engine-ktor                   | [apollographql/apollo-kotlin-ktor-support](https://github.com/apollographql/apollo-kotlin-ktor-support)                               |
| com.apollographql.apollo3:apollo-mockserver                         | com.apollographql.mockserver:apollo-mockserver              | [apollographql/apollo-kotlin-mockserver](https://github.com/apollographql/apollo-kotlin-mockserver)                                   |
| com.apollographql.apollo3:apollo-normalized-cache-incubating        | com.apollographql.cache:normalized-cache-incubating         | [apollographql/apollo-kotlin-normalized-cache-incubating](https://github.com/apollographql/apollo-kotlin-normalized-cache-incubating) |
| com.apollographql.apollo3:apollo-normalized-cache-api-incubating    | com.apollographql.cache:normalized-cache-incubating         | [apollographql/apollo-kotlin-normalized-cache-incubating](https://github.com/apollographql/apollo-kotlin-normalized-cache-incubating) |
| com.apollographql.apollo3:apollo-normalized-cache-sqlite-incubating | com.apollographql.cache:normalized-cache-sqlite-incubating  | [apollographql/apollo-kotlin-normalized-cache-incubating](https://github.com/apollographql/apollo-kotlin-normalized-cache-incubating) |
| com.apollographql.apollo3:apollo-runtime-java                       | com.apollographql.java:client                               | [apollographql/apollo-kotlin-java-support](https://github.com/apollographql/apollo-kotlin-java-support)                               |
| com.apollographql.apollo3:apollo-rx2-support-java                   | com.apollographql.java:rx2                                  | [apollographql/apollo-kotlin-java-support](https://github.com/apollographql/apollo-kotlin-java-support)                               |
| com.apollographql.apollo3:apollo-rx3-support-java                   | com.apollographql.java:rx3                                  | [apollographql/apollo-kotlin-java-support](https://github.com/apollographql/apollo-kotlin-java-support)                               |

Those new artifacts also use a new package name. You can usually guess it by removing `.apollo3`:

```kotlin
// Replace 
import com.apollographql.apollo3.mockserver.MockServer

// With
import com.apollographql.mockserver.MockServer
```

## apollo-runtime

### Fetch errors do not throw

<Caution>

Error handling changes are a behavior change that is not detected at compile time. Usages of `execute`, `toFlow` and `watch` must be updated to the new error handling or changed to their version 3 compat equivalent.

See [`executeV3` and `toFlowV3`](#migration-helpers) for temporary methods to help with the migration.

See [nullability](../advanced/nullability) for a quick peek at the future of GraphQL nullability and error handling.

</Caution>

In Apollo Kotlin 3, fetch errors like network errors, cache misses, and parsing errors were surfaced by throwing exceptions in `ApolloCall.execute()` and in Flows (`ApolloCall.toFlow()`, `ApolloCall.watch()`). 

This was problematic because it was a difference in how to handle GraphQL errors vs other errors. It was also easy to forget catching the exceptions. Uncaught network errors is by far the number one error reported by the [Google Play SDK Index](https://play.google.com/sdks). Moreover, throwing terminates a Flow and consumers would have to handle re-collection.

In Apollo Kotlin 4, a new field `ApolloResponse.exception` has been added and these errors are now surfaced by returning (for `execute()`) or emitting (for Flows) an `ApolloResponse` with a non-null `exception` instead of throwing it. 

Queries and mutations: 

```kotlin
// Replace
try {
  val response = client.query(MyQuery()).execute()
  if (response.hasErrors()) {
    // Handle GraphQL errors
  } else {
    // No errors
    val data = response.data
    // ...
  }
} catch (e: ApolloException) {
  // Handle fetch errors
}

// With
val response = client.query(MyQuery()).execute()
if (response.data != null) {
  // Handle (potentially partial) data
} else {
  // Something wrong happened
  if (response.exception != null) {
    // Handle fetch errors
  } else {
    // Handle GraphQL errors in response.errors
  }
}
```

Subscriptions:

```kotlin
// Replace
client.subscription(MySubscription()).toFlow().collect { response ->
  if (response.hasErrors()) {
    // Handle GraphQL errors
  }
}.catch { e ->
  // Handle fetch errors
}

// With
client.subscription(MySubscription()).toFlow().collect { response ->
  val data = response.data
  if (data != null) {
    // Handle (potentially partial) data
  } else {
    // Something wrong happened
    if (response.exception != null) {
      // Handle fetch errors
    } else {
      // Handle GraphQL errors in response.errors
    }
  }
}
```

Note that this is true for all `Flows`, including watchers. If you don't want to receive error responses (like cache misses), filter them out:

```kotlin
// Replace
apolloClient.query(query).watch()

// With
apolloClient.query(query).watch().filter { it.exception == null }
```

The lower level `ApolloStore` APIs are not changed and throw on cache misses or I/O errors.

### ApolloCompositeException is not thrown

When using the cache, Apollo Kotlin 3 throws `ApolloCompositeException` if no response is found. For an example, a `CacheFirst` fetch policy throws `ApolloCompositeException(cacheMissException, apolloNetworkException)` if both cache and network failed.

In those cases, Apollo Kotlin 4 throws the first exception and adds the second as a suppressed exception:

```kotlin
// Replace
if (exception is ApolloCompositeException) {
  val cacheMissException = exception.first
  val networkException = exception.second
}

// With
val cacheMissException = exception
val networkException = exception.suppressedExceptions.firstOrNull()
```

For more control over the exception, use `toFlow()` and collect the different `ApolloResponse`. 

### emitCacheMisses(Boolean) is removed

In Apollo Kotlin 3, when using the normalized cache, you could set `emitCacheMisses(Boolean)` to `true` to emit cache misses instead of throwing.

In Apollo Kotlin 4, cache misses always emit a response with `response.exception` containing a `CacheMissException`. `emitCacheMisses(Boolean)` has been removed.

With the `CacheFirst`, `NetworkFirst` and `CacheAndNetwork` policies, cache misses and network errors are now exposed in `ApolloResponse.exception`. 

### Migration helpers

To ease the migration from Apollo Kotlin 3, drop-in helpers functions are provided that restore the version 3 behavior:

* `ApolloCall.executeV3()`
* `ApolloCall.toFlowV3()`

Those helper functions:
* throw on fetch errors
* make `CacheFirst`, `NetworkFirst` and `CacheAndNetwork` policies ignore fetch errors.
* throw ApolloComposite exception if needed.

Because of the number of different options in version 3 and the complexity of error handling, these functions may not 100% match the version 3 behavior, especially in the advanced cases involving watchers. If you are in one of those cases, we strongly recommend using the version 4 functions that are easier to reason about.

### Non-standard HTTP headers are not sent by default

`X-APOLLO-OPERATION-NAME` and `X-APOLLO-OPERATION-ID` are non-standard headers and are not sent by default anymore. If you used them for logging purposes or if you are using Apollo Server [CSRF prevention](https://www.apollographql.com/docs/apollo-server/security/cors/#preventing-cross-site-request-forgery-csrf), you can add them back using an [ApolloInterceptor](https://www.apollographql.com/docs/kotlin/kdoc/apollo-runtime/com.apollographql.apollo.interceptor/-apollo-interceptor/index.html?query=interface%20ApolloInterceptor):

```kotlin
val apolloClient = ApolloClient.Builder()
    .serverUrl(mockServer.url())
    .addInterceptor(object : ApolloInterceptor {
      override fun <D : Operation.Data> intercept(request: ApolloRequest<D>, chain: ApolloInterceptorChain): Flow<ApolloResponse<D>> {
        return chain.proceed(request.newBuilder()
            .addHttpHeader("X-APOLLO-OPERATION-NAME", request.operation.name())
            .addHttpHeader("X-APOLLO-OPERATION-ID", request.operation.id())
            .build()
        )
      }
    })
    .build()
```

### ApolloCall.Builder.httpHeaders is additive

In Apollo Kotlin 3, if HTTP headers were set on an `ApolloCall`, they would replace the ones set on `ApolloClient`. In Apollo Kotlin 4 they are added instead by default. To replace them, call `ApolloCall.Builder.ignoreApolloClientHttpHeaders(true)`.

```kotlin
// Replace
val call = client.query(MyQuery())
  .httpHeaders(listOf("key", "value"))
  .execute()

// With
val call = client.query(MyQuery())
  .httpHeaders(listOf("key", "value"))
  .ignoreApolloClientHttpHeaders(true)
  .execute()
```

### HttpEngine implements Closeable

`HttpEngine` now implements `Closeable` and has its `dispose` method renamed to `close`. If you have a custom `HttpEngine`, you need to implement `close` instead of `dispose`.

## apollo-gradle-plugin

### Multi-module dependsOn

In Apollo Kotlin 3, depending on an upstream GraphQL module is done using the `apolloMetadata` configuration.

In Apollo Kotlin 4, this is now done using the `Service.dependsOn()` API. `Service.dependsOn()` works for multi-services repositories and, by hiding the configuration names, allows us to change the implementation in the future if needed. This is probably going to be required to accommodate [Gradle project isolation](https://docs.gradle.org/current/userguide/isolated_projects.html).   

```kotlin
// feature1/build.gradle.kts

// Replace
dependencies {
  // ...

  // Get the generated schema types (and fragments) from the upstream schema module 
  apolloMetadata(project(":schema"))

  // You also need to declare the schema module as a regular dependency
  implementation(project(":schema"))
}

// With
dependencies {
  // ...
  
  // You still need to declare the schema module as a regular dependency
  implementation(project(":schema"))
}

apollo {
  service("service") {
    // ...

    // Get the generated schema types and fragments from the upstream schema module 
    dependsOn(project(":schema"))
  }
}
```

### Auto-detection of used types

In multi-module projects, by default, all the types of an upstream module are generated because there is no way to know in advance what types are going to be used by downstream modules. For large projects this can lead to a lot of unused code and an increased build time.

To avoid this, in Apollo Kotlin 3 you could manually specify which types to generate by using `alwaysGenerateTypesMatching`. In Apollo Kotlin 4 you can opt in auto-detection of used types. 

To enable this, add the "opposite" link of dependencies with `isADependencyOf()`.

```kotlin
// schema/build.gradle.kts

// Replace
apollo {
  service("service") {
    // ...
    
    // Generate all the types in the schema module
    alwaysGenerateTypesMatching.set(listOf(".*"))

    // Enable generation of metadata for use by downstream modules 
    generateApolloMetadata.set(true)
  }
}

// With
apollo {
  service("service") {
    // ...

    // Enable generation of metadata for use by downstream modules 
    generateApolloMetadata.set(true)

    // Get used types from the downstream module1
    isADependencyOf(project(":feature1"))

    // Get used types from the downstream module2
    isADependencyOf(project(":feature2"))
    
    // ...
  }
}
```

If you were using `apolloUsedCoordinates`, you can also remove it:

```kotlin
dependencies {
  // Remove this 
  apolloUsedCoordinates(project(":feature1"))
}
```

### All schema types are generated in the schema module

Apollo Kotlin 3 allows to generate schema types for enums or input types in any module. While very flexible, this creates a lot of complexity:

- Duplicate types could be generated in sibling modules.
- It's unclear what package name to use. 
  - Using the downstream module package name means the qualified name of the class changes if the type is moved to another module.
  - Using the schema module package name can be surprising as it's not the one specified in the Gradle configuration.
- Gradle has APIs to aggregate all projects in a project isolation compatible way (see [gradle#22514](https://github.com/gradle/gradle/issues/22514)). But it's not clear yet how to do this for a subset of the projects (see [gradle#29037](https://github.com/gradle/gradle/issues/29037)).

Apollo Kotlin 4 enforces generating all schema types in a single module, the schema module. This restriction simplifies the Gradle plugin and makes it easier to debug and evolve that code. 

It also means that you can't publish your schema module with a subset of the types and let other modules generate their own in another repository. If you are publishing your schema module, it needs to contain all the schema types used by your consumers, either using `alwaysGenerateTypesMatching.set(listOf(".*"))` or manually adding them. 

If that is too much of a limitation, please open an issue with your use case and we will revisit.

### Apollo metadata publications are no longer created automatically

Apollo Kotlin 3 automatically creates an "apollo" publication using artifactId `${project.name}-apollo`. This created dependency resolution issues in some projects (see [apollo-kotlin#4350](https://github.com/apollographql/apollo-kotlin/issues/4350)).

Apollo Kotlin 4 exposes a software component instead of a publication to give more control over how publications are created. In order to publish the Apollo metadata, create a `MavenPublication` manually:

```kotlin
publishing {
  publications {
    create("apollo", MavenPublication::class.java) {
      from(components["apollo"])
      artifactId = "${project.name}-apollo"
    }
  }
}
```

### Custom scalars declaration

`customScalarsMapping` is removed and replaced with `mapScalar()` which makes it easier to map to built-in types and/or provide a compile type adapter for a given type:

```kotlin
// Replace
customScalarsMapping.set(mapOf(
    "Date" to "kotlinx.datetime.LocalDate"
))

// With
mapScalar("Date", "kotlinx.datetime.LocalDate")

// Replace
customScalarsMapping.put("MyLong", "kotlin.Long")

// With
mapScalarToKotlinLong("MyLong")

```

### Migrating to ApolloCompilerPlugin

Apollo Kotlin 4 introduces `ApolloCompilerPlugin` as a way to customize code generation. `ApolloCompilerPlugin` replaces compiler hooks and are loaded using the [ServiceLoader](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) API. They run in a separate classloader from your Gradle build. As a result, using  `Service.operationIdGenerator`/`Service.operationOutputGenerator` together with `ApolloCompilerPlugin` is not possible.

`Service.operationIdGenerator`/`Service.operationOutputGenerator` are deprecated and will be removed in a future release. You can migrate to `ApolloCompilerPlugin` using the instructions [from the dedicated page](https://www.apollographql.com/docs/kotlin/v4/advanced/compiler-plugins) and the `ApolloCompilerPlugin.operationIds()` method:

```kotlin
// Replace (in your build scripts) 
val operationOutputGenerator = object: OperationOutputGenerator {
  override fun generate(operationDescriptorList: Collection<OperationDescriptor>): OperationOutput {
    return operationDescriptorList.associateBy {
      it.source.sha1()
    }
  }
  override val version: String = "v1"
}

// Or, if using OperationIdGenerator, replace
val operationIdGenerator = object: OperationIdGenerator {
  override fun apply(operationDocument: String, operationName: String): String {
    return operationDocument.sha1()
  }

  override val version: String = "v1"
}

// With (in your compiler plugin module) 
class MyPlugin: ApolloCompilerPlugin {
  override fun operationIds(descriptors: List<OperationDescriptor>): List<OperationId>? {
    return descriptors.map {
      OperationId(it.source.sha1(), it.name)
    }
  }
}
```

### Operation manifest file location

Since Apollo Kotlin now supports [different operation manifest formats](../advanced/persisted-queries), the `operationOutput.json` file is generated in `"build/generated/manifest/apollo/$service/operationOutput.json"` instead of `"build/generated/operationOutput/apollo/$service/operationOutput.json"`.

### `useSchemaPackageNameForFragments` is removed

This was provided for compatibility with 2.x and is now removed. If you need specific package names for fragments, you can use a [compiler plugin](../advanced/compiler-plugins) and `ApolloCompilerPlugin.layout()` instead. 

## apollo-compiler

### "compat" `codegenModels` is removed

The `"compat"` codegen models was provided for compatibility with 2.x and is now removed. `"operationBased"` is more consistent and generates less code:

```kotlin
// Replace
apollo {
  service("service") {
    codegenModels.set("compat")
  }
}

// With
apollo {
  service("service") {
    codegenModels.set("operationBased")
  }
}
```

In the generated models, inline fragments accessors are prefixed with `on`:

```kotlin
// Replace
data.hero.asDroid

// With
data.hero.onDroid
```

The `fragments` synthetic property is not needed anymore:

```kotlin
// Replace
data.hero.fragments.heroDetails

// With
data.hero.heroDetails
```

Finally, some fields that were merged from inline fragments in their parents must now be accessed through the inline fragment:

```kotlin
// Replace
/**
 * {
 *   hero {
 *     # this condition is always true
 *     # allowing to merge the name field
 *     ... on Character {
 *       name
 *     }
 *   }
 * }
 */
data.hero.name

// With 
/**
 * name is not merged anymore
 */
data.hero.onCharacter?.name
```

<Note>

The [Android Studio plugin](../testing/android-studio-plugin#migration-helpers) provides a `compat` to `operationBased` migration tool which automates a lot of these changes.

</Note>

### Data builders

In Apollo Kotlin 3, using data builders with fragments required nesting the fields in a `buildFoo {}` block to determine the specific type returned. 

In Apollo Kotlin 4, this is specified by using the first paramter of the `Data()` constructor function. This allows the syntax for using data builders with fragments to be the same as for operations:

```kotlin
// Replace
val data = AnimalDetailsImpl.Data {
  buildCat {
    name = "Noushka"
    species = "Maine Coon"
  }
}

// With
val data = AnimalDetailsImpl.Data(Cat) {
  name = "Noushka"
  species = "Maine Coon"
}
```

See [data builders documentation](../testing/data-builders) for more details.

### Test builders are removed

Test builders were an experimental feature that have been superseded by [data builders](../testing/data-builders), a simpler version that also plays nicer with custom scalars.

In Apollo Kotlin 4, test builders are no longer available - please refer to the data builders documentation for more information.

### Enum class names now have their first letter capitalized

For consistency with other types, GraphQL enums are now capitalized in Kotlin. You can restore the previous behavior using `@targetName`:

```graphql
# Make sure `someEnum` isn't renamed to `SomeEnum`
enum someEnum @targetName(name: "someEnum"){
  A
  B
}
```

### `__Schema` is in the `schema` subpackage

When using the `generateSchema` option, to avoid a name clash with the introspection type of the same name, the `__Schema` type is now generated in a `schema` subpackage (instead of `type`):

```kotlin
// Replace
import com.example.type.__Schema

// With
import com.example.schema.__Schema
```

### KotlinLabs directives are now at version 0.3

The embedded [kotlin_labs](https://specs.apollo.dev/kotlin_labs/v0.3/) directives are now at version 0.3.

These are the client directives supported by Apollo Kotlin. They are imported automatically but if you relied on an explicit import for renames or another reason, you'll need to bump the version:

```graphql
# Replace
extend schema @link(url: "https://specs.apollo.dev/kotlin_labs/v0.1/", import: ["@typePolicy"])

# With
extend schema @link(url: "https://specs.apollo.dev/kotlin_labs/v0.3/", import: ["@typePolicy"])
```

This is a backward compatible change.

### Directive usages are validated 

In Apollo Kotlin 4, all directive usages are validated against their definition. If you have queries using client side directives:

```graphql
query HeroName {
  hero {
    # WARNING: '@required' is not defined by the schema
    name @required
  }
}
```

Those queries are now required to have a matching directive definition in the schema. If you downloaded your schema using introspection, this shouldn't be an issue.

In some cases, for client directives and/or if you did not use introspection, the directive definition might be missing. For those cases, you can add it explicitly in a `extra.graphqls` file:

```graphql
directive @required on FIELD
```

### Sealed class `UNKNOWN__` constructors are private

When generating enums as sealed classes with the `sealedClassesForEnumsMatching` option, the `UNKNOWN__` constructor is now generated as private, to prevent its accidental usage.

It is recommended to update your schema instead of instantiating an `UNKNOWN__` value, but if you need to, use the `safeValueOf` method instead:

```kotlin
// Replace
val myEnum = MyEnum.UNKNOWN__("foo")

// With
val myEnum = MyEnum.safeValueOf("foo")
```

## apollo-normalized-cache

### Configuration order

The normalized cache must be configured before the auto persisted queries, configuring it after will now fail (see https://github.com/apollographql/apollo-kotlin/pull/4709).

```kotlin
// Replace
val apolloClient = ApolloClient.Builder()
  .serverUrl(...)
  .autoPersistedQueries(...)
  .normalizedCache(...)
  .build()

// With
val apolloClient = ApolloClient.Builder()
  .serverUrl(...)
  .normalizedCache(...)
  .autoPersistedQueries(...)
  .build()
```

## apollo-ast

In Apollo Kotlin 3, apollo-ast uses [Antlr](https://www.antlr.org/) to parse GraphQL documents.

In Apollo Kotlin 4, apollo-ast uses its own recursive descent parser. Not only does it remove a dependency but it's also faster and supports KMP. We took this opportunity to clean the apollo-ast API:

* The AST classes (`GQLNode` and subclasses) as well as `Introspection` classes are not data classes anymore (see https://github.com/apollographql/apollo-kotlin/pull/4704/).
* The class hierarchy has been tweaked so that `GQLNamed`, `GQLDescribed` and `GQLHasDirectives` are more consistently inherited from.
* `GQLSelectionSet` and `GQLArguments` are removed from `GQLField` and `GQLInlineFragment`. Use `.selections` directly.
* `GQLInlineFragment.typeCondition` is now nullable to account for inline fragments who inherit their type condition.
* `SourceLocation.position` is renamed to `SourceLocation.column` and is now 1-indexed. 
* `GQLNode.sourceLocation` is now nullable to account for the cases where the nodes are constructed programmatically.
* `File.toSchema()` and `String.toSchema()` are removed. Instead use `toGQLDocument().toSchema()`.

## Example of a migration

If you are looking for inspiration, we updated the [version 3 integration tests to use version 4](https://github.com/apollographql/apollo-kotlin/pull/5418/). If you have an open source project that migrated, feel free to share it, and we'll include it here.
